#pragma once
#include <iostream>
#include<unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<sys/stat.h>
#include <string>
#include <strings.h>
#include <netinet/in.h>
#include<cstring>
#include<cerrno>
#include <arpa/inet.h>
#include<functional>
#include<fcntl.h>

using namespace std;
enum
{
    SOCKET_ERR,
    BIND_ERR,
    USAGE_ERR,
    SENDTO_ERR,
    OPEN_ERR
};


namespace udpsever
{



#define defaultip "0.0.0.0"

    class sever
    {
    public:
        using func_t = function<void(int,string&,string&,uint16_t)>;
    public:
        sever(uint16_t& port,func_t callback)
            : _port(port), _ip(defaultip), _sock(-1),_callback(callback)
        {
        }

        void init()
        {
            _sock = socket(AF_INET, SOCK_DGRAM, 0);
            // 判断是否创建成功
            if (_sock == -1)
            {
                cerr << "socket error" << endl;
                exit(SOCKET_ERR);
            }
            struct sockaddr_in addr;
            // 由于struct sockaddr_in中有8字节的填充，避免出现随机值影响结果，我们要先将所有的字节设为0
            bzero(&addr, sizeof addr);
            addr.sin_family = AF_INET;
            addr.sin_port = htons(_port);
            addr.sin_addr.s_addr = inet_addr(_ip.c_str());
            // 绑定
            int n = bind(_sock, (struct sockaddr *)&addr, sizeof addr);
            // 成功返回0，失败返回-1
            if (n == -1)
            {
                cerr << "bind failed" << endl;
                cout<<strerror(errno)<<endl;
                exit(BIND_ERR);
            }
            (void)n;
        }

        void start()
        {
            char buf[1024];
            while(1)
            {
                bzero(buf,1024); 
                struct sockaddr_in from;
                socklen_t fromlen = sizeof from;
                ssize_t n = recvfrom(_sock,buf,sizeof buf -1,0,(struct sockaddr*)&from,&fromlen);
                buf[n]=0;  //我们把数据当成字符串来处理，所以最后要留一个位置放 \0 
                string message = buf ;//收到的消息
                string ip = inet_ntoa(from.sin_addr); //将ip 转换成主机序列的点分十进制
                uint16_t port = ntohs(from.sin_port);
                _callback(_sock,message,ip,port);
            }
        }
        ~sever()
        {
            close(_sock);
        }
    public:
    private:
        uint16_t _port;
        string _ip; // 我们接受点分十进制的字符串类型的ip
        int _sock;
        func_t _callback;
    };

}
